package cn.com.easy.utils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import cn.com.easy.exception.BusinessException;

/**
 * 负载均衡工具类
 * 
 * @author nibili 2017年11月27日
 * 
 */
public class LoadBalanceUtils {

	public static void main(String[] args) throws BusinessException {

		List<String> list = new ArrayList<String>();
		list.add("192.168.1.1");
		list.add("192.168.1.2");
		list.add("192.168.1.3");
		list.add("192.168.1.4");
		// 一
		LoadBalanceRoundRobin<String> loadBalanceRoundRobin = new LoadBalanceRoundRobin<String>(list);
		for (int i = 0; i < 20; i++) {
			System.out.println("轮询法：" + loadBalanceRoundRobin.getServer());
		}
		// 二
		LoadBalanceRandom<String> loadBalanceRandom = new LoadBalanceRandom<String>(list);
		for (int i = 0; i < 20; i++) {
			System.out.println("随机法：" + loadBalanceRandom.getServer());
		}
		// 三
		LoadBalanceHash<String> loadBalanceHash = new LoadBalanceHash<String>(list);
		for (int i = 0; i < 20; i++) {
			System.out.println("源地址哈希法" + loadBalanceHash.getServer("192.168.1." + i));
		}

		//
		HashMap<String, Integer> hashMap = new HashMap<String, Integer>();
		hashMap.put("192.168.1.1", 4);
		hashMap.put("192.168.1.3", 5);
		hashMap.put("192.168.1.4", 8);

		// 四
		LoadBalanceWeightRoundRobin<String> loadBalanceWeightRoundRobin = new LoadBalanceWeightRoundRobin<String>(hashMap);
		for (int i = 0; i < 20; i++) {
			System.out.println("加权轮询法" + loadBalanceWeightRoundRobin.getServer());
		}

		// 五
		LoadBalanceWeightRandom<String> loadBalanceWeightRandom = new LoadBalanceWeightRandom<String>(hashMap);
		for (int i = 0; i < 20; i++) {
			System.out.println("加权随机法" + loadBalanceWeightRandom.getServer());
		}
	}

	/**
	 * 一、轮询（Round Robin）法
	 * 
	 * @author nibili 2017年11月27日
	 * 
	 */
	public static class LoadBalanceRoundRobin<T> {
		/** Map位置 */
		private Integer pos = 0;
		/** 服务器List */
		private List<T> serverList = new ArrayList<T>();
		/** 服务器数量 */
		int serverListSize = 1;

		public LoadBalanceRoundRobin(List<T> serverList) throws BusinessException {
			// 新的list，避免服务器的上下线导致的并发问题
			if (serverList == null || serverList.size() <= 0) {
				throw new BusinessException("Hello,服务器列表不能为空");
			}
			this.serverList.addAll(serverList);
			serverListSize = serverList.size();
		}

		/**
		 * 轮询（Round Robin）法
		 * 
		 * @return
		 * @author nibili 2017年11月27日
		 */
		public T getServer() {
			// 取得Ip地址List
			T server = null;
			synchronized (pos) {
				if (pos >= serverListSize) {
					pos = 0;
				}
				server = serverList.get(pos);
				pos++;
			}
			return server;
		}

	}

	/**
	 * 二、随机（Random）法
	 * 
	 * @author nibili 2017年11月27日
	 * 
	 */
	public static class LoadBalanceRandom<T> {

		/** 随机对象 */
		private java.util.Random random = new java.util.Random();
		/** 服务器List */
		private List<T> serverList = new ArrayList<T>();
		/** 服务器数量 */
		int serverListSize = 1;

		public LoadBalanceRandom(List<T> serverList) throws BusinessException {
			if (serverList == null || serverList.size() <= 0) {
				throw new BusinessException("Hello,服务器列表不能为空");
			}
			this.serverList.addAll(serverList);
			serverListSize = serverList.size();
		}

		/**
		 * 随机（Random）法
		 * 
		 * @return
		 * @author nibili 2017年11月27日
		 */
		public T getServer() {
			int randomPos = random.nextInt(serverListSize);
			return serverList.get(randomPos);
		}
	}

	/**
	 * 三、源地址哈希（Hash）法
	 * 
	 * @author nibili 2017年11月27日
	 * 
	 */
	public static class LoadBalanceHash<T> {

		/** 服务器List */
		private List<T> serverList = new ArrayList<T>();
		/** 服务器数量 */
		int serverListSize = 1;

		public LoadBalanceHash(List<T> serverList) throws BusinessException {
			if (serverList == null || serverList.size() <= 0) {
				throw new BusinessException("Hello,服务器列表不能为空");
			}
			this.serverList.addAll(serverList);
			serverListSize = serverList.size();
		}

		/**
		 * 源地址哈希（Hash）法
		 * 
		 * @param remoteIp
		 * @return
		 * @author nibili 2017年11月27日
		 */
		public T getServer(String remoteIp) {
			// 在Web应用中可通过HttpServlet的getRemoteIp方法获取
			int hashCode = remoteIp.hashCode();
			int serverPos = hashCode % serverListSize;
			return serverList.get(serverPos);
		}
	}

	/**
	 * 四、加权轮询（Weight Round Robin）法
	 * 
	 * @author nibili 2017年11月27日
	 * 
	 */
	public static class LoadBalanceWeightRoundRobin<T> {

		/** */
		private Integer pos = 0;
		/** 待路由的Ip列表，Key代表Ip，Value代表该Ip的权重 */
		private HashMap<T, Integer> serverWeightMap = new HashMap<T, Integer>();
		/** 服务器列表 */
		private List<T> serverList = new ArrayList<T>();
		/** 服务器数量 */
		int serverListSize = 1;

		public LoadBalanceWeightRoundRobin(HashMap<T, Integer> serverWeightMap) throws BusinessException {
			if (serverWeightMap == null || serverWeightMap.size() <= 0) {
				throw new BusinessException("Hello,服务器列表不能为空");
			}
			this.serverWeightMap.putAll(serverWeightMap);
			Set<T> keySet = this.serverWeightMap.keySet();
			Iterator<T> iterator = keySet.iterator();
			while (iterator.hasNext()) {
				T server = iterator.next();
				int weight = this.serverWeightMap.get(server);
				for (int i = 0; i < weight; i++)
					serverList.add(server);
			}
			serverListSize = serverList.size();
		}

		/**
		 * 加权轮询（Weight Round Robin）法
		 * 
		 * @return
		 * @author nibili 2017年11月27日
		 */
		public T getServer() {
			T server = null;
			synchronized (pos) {
				if (pos >= serverListSize)
					pos = 0;
				server = serverList.get(pos);
				pos++;
			}
			return server;
		}
	}

	/**
	 * 五、加权随机（Weight Random）法
	 * 
	 * @author nibili 2017年11月27日
	 * 
	 */
	public static class LoadBalanceWeightRandom<T> {

		/** 随机对象 */
		private java.util.Random random = new java.util.Random();
		/** 待路由的Ip列表，Key代表Ip，Value代表该Ip的权重 */
		private HashMap<T, Integer> serverWeightMap = new HashMap<T, Integer>();
		/** 服务器列表 */
		private List<T> serverList = new ArrayList<T>();
		/** 服务器数量 */
		int serverListSize = 1;

		public LoadBalanceWeightRandom(HashMap<T, Integer> serverWeightMap) throws BusinessException {
			if (serverWeightMap == null || serverWeightMap.size() <= 0) {
				throw new BusinessException("Hello,服务器列表不能为空");
			}
			this.serverWeightMap.putAll(serverWeightMap);
			Set<T> keySet = this.serverWeightMap.keySet();
			Iterator<T> iterator = keySet.iterator();
			while (iterator.hasNext()) {
				T server = iterator.next();
				int weight = this.serverWeightMap.get(server);
				for (int i = 0; i < weight; i++)
					serverList.add(server);
			}
			serverListSize = serverList.size();
		}

		/**
		 * 加权轮询（Weight Round Robin）法
		 * 
		 * @return
		 * @author nibili 2017年11月27日
		 */
		public T getServer() {
			int randomPos = random.nextInt(serverListSize);
			return serverList.get(randomPos);
		}
	}

}
